package com.syl.starter.itools.common.utils;

import com.syl.starter.itools.base.exception.ReflectException;
import com.syl.starter.itools.base.utils.LogUtils;
import com.syl.starter.itools.common.annotations.BeanConvert;
import com.syl.starter.itools.common.annotations.EditField;
import com.syl.starter.itools.common.annotations.Relevance;
import com.syl.starter.itools.common.enums.RelevanceTypeEnum;
import com.syl.starter.itools.common.enums.RoundModeEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;

/**
 * bean对象处理工具类
 *
 * @author syl
 * @create 2018-10-18 10:07
 **/
public class BeanUtils extends org.springframework.beans.BeanUtils {
    private static final String ENUM_VALUE_METHOD = "valueOf";
    private static Logger LOG = LoggerFactory.getLogger(BeanUtils.class);

    /**
     * 合并bean 对象 仅限定相同类
     * 适用场景 soure 是默认对象 target 是扩展对象 调用后target会包含默认设置的值，同字段以target为准
     * 同jQuery.extend
     *
     * source: {"name", "demo", time: "2018"} target {"name": "test", "now": "2019"}
     * print: {"name": "test", time: "2018", "now": "2019"}
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static boolean mergeBean(Object source, Object target){
        if (target == null || source == null)
            return false;
        copyProperties(source, target, getValuePropertyNames(target));
        return true;
    }

    /**
     * 获取所有有值的字段
     * @param source
     * @return
     */
    public static String[] getValuePropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (null != srcValue) emptyNames.add(pd.getName());
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 转换bean对象
     * @param source 源数据bean对象
     * @param target 要转换到的目标对象
     *
     */
    public static void convert(Object source, Object target) throws ReflectException {
        BeanConvert convert = target.getClass().getAnnotation(BeanConvert.class);
        Field[] fields = ReflectUtils.getPrivateField(source.getClass());
        String[] ignore = convert == null ? new String[]{} : convert.ignore();
        List<String> ignoreList = Arrays.asList(ignore); // 配置的忽略字段
        for (Field field : fields) {// 遍历数据源字段
            String name = field.getName();
            if(ignoreList.contains(name))
                continue;
            EditField annotation = field.getAnnotation(EditField.class);
            String getPrefix = annotation == null ? null : annotation.getPrefix();
            Object fieldValue = ReflectUtils.getFieldValue(source, getPrefix, name, field.getType());
            if(fieldValue == null) // 忽略空字段
                continue;
            analyzeBean(field, fieldValue, target);
        }
    }

    /**
     * 把简单的java bean 转换为map类型
     * @param target 简单类型的java bean 不能有bean嵌套
     * @param cz map 类型
     * @return
     * @throws ReflectException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static Map<String, Object> convertToMap(Object target, Class<? extends Map> cz) throws ReflectException, InstantiationException, IllegalAccessException {
        Class<?> targetClass = target.getClass();
        Field[] privateField = ReflectUtils.getPrivateField(targetClass);
        Map<String, Object> map = ReflectUtils.instantiation(cz);
        for (Field field : privateField) {
            if (field == null) continue;
            String name = field.getName();
            Object fieldValue = ReflectUtils.getFieldValue(target, name);
            if (fieldValue == null) continue;
            map.put(name, fieldValue);
        }
        return map;
    }

    /**
     * 分析属性和注解
     * @param sourceField
     * @param fieldValue
     * @param target
     */
    private static void analyzeBean(Field sourceField, Object fieldValue, Object target) throws ReflectException {
        Field[] declaredFields = ReflectUtils.getPrivateField(target.getClass());
        for (Field targetField : declaredFields) {
            EditField annotation = targetField.getAnnotation(EditField.class);
            String targetName = targetField.getName();
            String sourceName = sourceField.getName();
            if(annotation != null && !StringUtils.isEmpty(annotation.alias())){ // 匹配别名
                targetName = annotation.alias();
            }
            if(!targetName.equals(sourceName))
                continue;
            transform(sourceField, fieldValue, target, targetField);
        }
    }

    /**
     * 字段转换
     * @param sourceField
     * @param fieldValue
     * @param target
     * @param targetField
     */
    private static void transform(Field sourceField, Object fieldValue, Object target, Field targetField) throws ReflectException {
        EditField annotation = targetField.getAnnotation(EditField.class);
        if(annotation == null) {// 无注解直接反射
            reflectHelp(targetField, target,  sourceField.getName(), fieldValue);
            return;
        }
        String format = annotation.format();
        RoundModeEnum roundMode = annotation.roundMode();
        String prefix = annotation.prefix();
        String suffix = annotation.suffix();
        String targetFieldName = targetField.getName();
        if(fieldValue instanceof Date){
            if(StringUtils.isEmpty(format)){ // 未设置格式 原样输出
                reflectHelp(targetField, target, targetFieldName, fieldValue);
                return;
            }
            // 按指定日期格式格式化
            String formatDate = new DateUtils((Date) fieldValue).format(format);
            reflectHelp(targetField, target,  targetFieldName, prefix + formatDate + suffix);
            return;
        }
        if(fieldValue instanceof Float || fieldValue instanceof Double || fieldValue instanceof BigDecimal){
            if(StringUtils.isEmpty(format)) { // 未设置格式 原样输出
                reflectHelp(targetField, target, targetFieldName, fieldValue);
                return;
            }
            if(fieldValue instanceof BigDecimal){
                String formatString = DecimalUtils.format((BigDecimal) fieldValue, format, roundMode);
                reflectHelp(targetField, target,  targetFieldName, prefix + formatString + suffix);
                return;
            }
            String formatString = DecimalUtils.format(fieldValue, format, roundMode, true);
            reflectHelp(targetField, target,  targetFieldName, prefix + formatString + suffix);
            return;
        }
        if(fieldValue instanceof String){
            reflectHelp(targetField, target, targetFieldName, prefix + fieldValue + suffix);
            return;
        }
        // 不匹配类型 原样输出
        reflectHelp(targetField, target,  targetFieldName, fieldValue);
    }


    private static void reflectHelp(Field targetField,Object entity,String field, Object fieldValue) throws ReflectException {
        Relevance relevance = targetField.getAnnotation(Relevance.class);
        if(relevance == null) {
            ReflectUtils.setFieldValue(entity, field, fieldValue);
            return;
        }
        String fieldName = relevance.fieldName();
               fieldName = StringUtils.isEmpty(fieldName) ? field : fieldName;
        RelevanceTypeEnum type = relevance.type();
        if(RelevanceTypeEnum.ENUM == type){
            Object enumValue = getEnumValue(relevance, fieldName, fieldValue);
            ReflectUtils.setFieldValue(entity, field, enumValue);
        }
//        if(RelevanceTypeEnum.JAVA_BEAN == type){
//            Object beanValue = getJavaBean(relevance, fieldName, fieldValue);
//            ReflectUtils.setFieldValue(entity, field, beanValue);
//        }
    }

    /**
     * 类型为枚举时处理
     * @param relevance
     * @param fieldName
     */
    private static Object getEnumValue(Relevance relevance, String fieldName, Object fieldValue){
        Class sourceClass = relevance.source();
        String value = relevance.value();
        String tempMethodName = relevance.methodName();
        String methodName = null;
        try {
            if(!StringUtils.isEmpty(value)) {
                methodName = String.format(tempMethodName, StringUtils.firstCharUpper(value));
                // 获取目标枚举
                Object invokeGetObj = ReflectUtils.methodInvoke(sourceClass, methodName, new Object[]{fieldValue}, sourceClass);
                return ReflectUtils.getFieldValue(invokeGetObj, fieldName, Object.class);
            }
            methodName = ENUM_VALUE_METHOD;
            // 获取目标枚举
            Object methodInvoke = ReflectUtils.methodInvoke(sourceClass, ENUM_VALUE_METHOD, new Object[]{fieldValue}, sourceClass);
            return ReflectUtils.getFieldValue(methodInvoke, fieldName, Object.class);
        } catch (InvocationTargetException e) {
            LogUtils.error(LOG, "目标方法调用失败 方法{}", e, methodName);
        } catch (IllegalAccessException e) {
            LogUtils.error(LOG, "目标方法调用失败 方法{}", e, methodName);
        } catch (ReflectException e) {
            LogUtils.error(LOG, "目标枚举缺少 {} get方法 ", e, fieldName);
        }
        return null;
    }

}
